package com.hadoop.util.address;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class OptionMap {

    public static final String NAME = "name";//中文名称
    public static final String EN = "en";//英文名称
    public static final String ST = "st";//缩写
    public static final String CODE = "code";//代码
    public static final String LAT = "lat";//纬度
    public static final String LNG = "lng";//经度

    /**
     * 地区类型
     */
    public enum AreaType {
        PROV, CITY, TOWN;
    }


    // 直辖市
    private final static List<Integer> CENTRAL_CITY_LIST = Lists.newArrayList(10000000, 11000000, 12000000, 13000000);

    public static Map<String, String> setMap(final String name, final String en,final String st) {
        return ImmutableMap.of(OptionMap.NAME, name, OptionMap.EN, en,OptionMap.ST,st);
    }

    /**
     * <p>
     * 返回完整的城市镇区地址，默认取中文地址。例：广东东莞莞城区
     * </p>
     *
     * @param key
     *            地区编码
     * @return
     */
    public static String getFullAddr(Integer key) {
        return getFullAddr(key, null);
    }

    /**
     * <p>
     * 返回完整的城市镇区地址
     * </p>
     *
     * @param key
     *            地区编码
     * @param type
     *            显示类型：name-中文地址,en-英文地址
     * @return
     */
    public static String getFullAddr(Integer key, String type) {
        return getProvStr(key, type) + getCityStr(key, type, false) + getTownStr(key, type);
    }

    public static String getProvAddr(Integer key) {
        return getProvStr(key, null);
    }

    /**
     * 取得城市+镇区组成的字串 : 东莞南城区
     * @param key  地区代码
     * @return
     */
    public static String getCityAndTownStr(Integer key) {
        if(key == null || key < 0){
            return "";
        }
        return getCityAddr(key) + getTownAddr(key);
    }

	/**
     * <p>
     * 返回所在城市地址，默认取中文地址 。例：东莞<br>
     * 说明：<br>
     * 如果当前key是省编码，则返回省<br>
     * 如果当前key是市编码，则返回市<br>
     * </p>
     *
     * @param key
     *            地区编码
     * @return
     */
    public static String getCityAddr(Integer key) {
        return getCityAddr(key, null);
    }

    /**
     * <p>
     * 返回所在城市地址<br>
     * 说明：<br>
     * 如果当前key是省编码，则返回省<br>
     * 如果当前key是市编码，则返回市<br>
     * </p>
     *
     * @param key
     *            地区编码
     * @param type
     *            显示类型：name-中文地址,en-英文地址
     * @return
     */
    public static String getCityAddr(Integer key, String type) {
        return getCityStr(key, type, true);
    }

    /**
     * <p>
     * 返回镇区地址，默认取中文地址。例：莞城区
     * </p>
     *
     * @param key
     *            地区编码
     * @return
     */
    public static String getTownAddr(Integer key) {
        return getTownAddr(key, null);
    }

    /**
     * <p>
     * 返回镇区地址
     * </p>
     *
     * @param key
     *            地区编码
     * @param type
     *            显示类型：name-中文地址,en-英文地址
     * @return
     */
    public static String getTownAddr(Integer key, String type) {
        return getTownStr(key, type);
    }

    /**
     * <p>
     * 返回地区编码映射地址，默认取中文地址<br>
     * 例如：14000000-广东，14010000-东莞 <br>
     * </p>
     *
     * @param key
     *            地区编码
     * @return
     */
    public static String getAddrWithCityCode(Integer key) {
        return getAddrWithCityCode(key, null);
    }

    /**
     * <p>
     * 返回地区编码映射地址<br>
     * <p>
     *
     * @param key
     *            地区编码
     * @param type
     *            显示类型：name-中文地址,en-英文地址
     * @return
     */
    public static String getAddrWithCityCode(Integer key, String type) {
        if (key != null && ObjectUtils.toString(key, "").length() >= 4) {
            Map<Integer, Map<String, String>> cityMap = OptionCityMap.OPT_MAP_CITY;
            Integer cityCode = formatCityCode(getCityCodeBits(String.valueOf(key)));
            Map<String, String> cityDataMap = cityMap.get(cityCode);
            if (cityDataMap != null) {
                return cityDataMap.get(StringUtils.defaultIfBlank(type, "name"));
            } else {
                Map<Integer, Map<String, String>> townMap = OptionTownMap.OPT_MAP_TOWN;
                Map<String, String> townDataMap = townMap.get(cityCode);
                if (townDataMap != null) {
                    return townDataMap.get(StringUtils.defaultIfBlank(type, "name"));
                }
            }
        }
        return "";
    }

    /**
     * <p>
     * 根据地址字符串返回城市编码，默认精确到镇区<br>
     * 例:广东省东莞市南城区，返回14010300<br>
     * </p>
     *
     * @param addr
     *            地址字符串
     * @return
     */
    public static Integer getCityCodeByAddr(String addr) {
        return getCityCodeByAddr(addr, AreaType.TOWN);
    }

    /**
     * <p>
     * 根据地址字符串返回城市编码
     * </p>
     *
     * @param addr
     *            地址字符串
     * @param areaType
     *            返回编码精度
     * @return
     */
    public static Integer getCityCodeByAddr(String addr, AreaType areaType) {
        List<Integer[]> matchedList = Lists.newArrayList();
        Integer cityCode = getCityCodeByAddr(addr, null, null);
        while (cityCode > 0) {
            Integer[] matchedCodeArr = getMatchedCityCode(cityCode, addr);
            if (areaType.equals(AreaType.TOWN) && matchedCodeArr[2] > 0) {
                return matchedCodeArr[2];
            }
            if (areaType.equals(AreaType.CITY) && matchedCodeArr[1] > 0) {
                return matchedCodeArr[1];
            }
            if (areaType.equals(AreaType.PROV) && matchedCodeArr[0] > 0) {
                return matchedCodeArr[0];
            }
            matchedList.add(matchedCodeArr);
            cityCode = getCityCodeByAddr(addr, cityCode + 1, null);
        }

        for (Integer[] matchedCodeArr : matchedList) {
            if (matchedCodeArr[1] > 0) {
                return matchedCodeArr[1];
            }
        }

        return CollectionUtils.isNotEmpty(matchedList) ? matchedList.get(0)[0] : 0;
    }

    /**
     * <p>
     * 根据城市名称返回完全匹配城市编码<br>
     * 例：东莞，返回14010000<br>
     * </p>
     *
     * @param value
     *            城市名称
     * @return
     */
    public static Integer getCityCodeByValue(String value) {
        if (StringUtils.isNotBlank(value)) {
            Map<Integer, Map<String, String>> cityMap = OptionCityMap.OPT_MAP_CITY;
            for (Map.Entry<Integer, Map<String, String>> entry : cityMap.entrySet()) {
                Integer code = entry.getKey();
                Map<String, String> cityDataMap = entry.getValue();
                if (StringUtils.equals(value, cityDataMap.get("name"))) {
                    return code;
                }
            }
        }
        return 0;
    }

    /**
     * 从城市值及类别取得城市代码
     * 例如: 输入：dongguan，en -> 14010000
     * @param value
     * @param type
     * @return
     */
    public static Integer getCityCodeByValue(String value,String type) {
        if (StringUtils.isNotBlank(value)) {
            Map<Integer, Map<String, String>> cityMap = OptionCityMap.OPT_MAP_CITY;
            for (Map.Entry<Integer, Map<String, String>> entry : cityMap.entrySet()) {
                Integer code = entry.getKey();
                Map<String, String> cityDataMap = entry.getValue();
                if (StringUtils.equals(value, cityDataMap.get(type))) {
                    return code;
                }
            }
        }
        return 0;
    }

    /**
     * 输入城市拼音得到城市中文名称
     * @param cityName
     * @return
     */
    public static String getCityStrByEnName(String cityName) {
        if (StringUtils.isNotBlank(cityName)) {
            Integer cityCode = getCityCodeByValue(cityName, "en");
            return getCityStr(cityCode, "name", true);
        }
        return "";
    }

    /**
     * 输入城市拼音+镇区拼音,得到地址
     * @param cityName
     * @return
     */
    public static String getTownStrByEnName(String cityName,String townName) {
        if (StringUtils.isNotBlank(cityName) &&  StringUtils.isNotBlank(townName)) {
            Integer cityCode = getCityCodeByValue(cityName, "en");
            Integer townCode = getTownCodeByCityCodeAndTownEn(cityCode, townName);
            return getTownStr(townCode, "name");
        }
        return "";
    }


    /**
     * 从城市代码 + 镇区拼音,取得镇区的代码
     * 东莞:1401 莞城区:guancheng
     * 例如: 输入：1401，guanchen -> 14010100
     * @param cityCode
     * @param townEnName
     * @return
     */
    public static Integer getTownCodeByCityCodeAndTownEn(Integer cityCode,String townEnName) {
        Integer townCode = 0;
        if (cityCode == null || StringUtils.isBlank(townEnName)) {
            return townCode;
        }
        cityCode = NumberUtils.toInt(OptionMap.getCityCodeBits(cityCode));
        List<Map<String,String>> townList = OptionTownMap.OPT_MAP_CITY_TWON.get(cityCode);
        if (CollectionUtils.isNotEmpty(townList)) {
            for (Map<String, String> map : townList) {
                if (StringUtils.equalsIgnoreCase(map.get("en"), townEnName)) {
                    townCode = NumberUtils.toInt(String.valueOf(map.get("code")));
                    break;
                }
            }
        }
        return townCode;
    }

    /**
     * <p>
     * 格式化城市编码，如果编码数据异常则返回默认编码0<br>
     * 兼容旧版4位编码，全部升级到8位编码<br>
     * </p>
     *
     * @param key
     *            地区编码
     * @return
     */
    public static Integer formatCityCode(Object key) {
        return formatCityCode(key, 0);
    }

    /**
     * <p>
     * 格式化城市编码<br>
     * 兼容旧版4位编码，全部升级到8位编码<br>
     * </p>
     *
     * @param key
     *            地区编码
     * @param defaultCode
     *            默认编码
     * @return
     */
    public static Integer formatCityCode(Object key, Integer defaultCode) {
        return NumberUtils.toInt(ObjectUtils.toString(key), 0) > 0 ? NumberUtils.toInt(StringUtils.rightPad(
                ObjectUtils.toString(key), 8, "0")) : defaultCode;
    }

    /**
     * <p>
     * 获取2位省份编码<br>
     * 例：14010000，从左边取2位编码，返回14<br>
     * </p>
     *
     * @param key
     *            地区编码
     * @return
     */
    public static String getProvCodeBits(Object key) {
        return StringUtils.length(ObjectUtils.toString(key)) >= 4 ? StringUtils.substring(ObjectUtils.toString(key), 0,
                2) : StringUtils.EMPTY;
    }

    /**
     * <p>
     * 获取4位城市编码<br>
     * 例：14010000，从左边取4位编码，返回1401<br>
     * </p>
     *
     * @param key
     *            地区编码
     * @return
     */
    public static String getCityCodeBits(Object key) {
        return StringUtils.length(ObjectUtils.toString(key)) >= 4 ? StringUtils.substring(ObjectUtils.toString(key), 0,
                4) : StringUtils.EMPTY;
    }

    /**
     * <p>
     * 判断是否直辖市<br>
     * 例：10000000，返回true<br>
     * </p>
     *
     * @param key
     *            地区编码
     * @return true-是 false-否
     */
    public static boolean isCentralCity(Integer key) {
        return CENTRAL_CITY_LIST.contains(formatCityCode(key));
    }

    /**
     * <p>
     * 判断是否直辖市，完全匹配城市名称<br>
     * 例：上海，返回true<br>
     * </p>
     *
     * @param city
     *            城市名称
     *
     * @return
     */
    public static boolean isCentralCity(String city) {
        if (StringUtils.isNotBlank(city)) {
            for (Integer key : CENTRAL_CITY_LIST) {
                if (city.equals(getCityAddr(key))) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 根据地址字符串找到一个匹配的地区编码
     *
     * @param key
     *            地区编码
     * @param addr
     *            地址字符串
     * @return 返回一维数组，[0]=省编码,[1]=市编码,[2]=镇区编码
     */
    private static Integer[] getMatchedCityCode(Integer key, String addr) {
        Integer[] cityCodeArr = new Integer[]{0, 0, 0};
        if (StringUtils.substring(String.valueOf(key), 2, 4).equals("00")) {
            cityCodeArr[0] = key;
            // 直辖市
            if (CENTRAL_CITY_LIST.contains(key)) {
                cityCodeArr[1] = key;
            } else {
                addr = StringUtils.replaceOnce(addr, getCityAddr(key), "");
                Integer rangeLeft = formatCityCode(key / 1000000) + 1;
                Integer rangeRight = formatCityCode(key / 1000000) + 999999;
                cityCodeArr[1] = getCityCodeByAddr(addr, rangeLeft, rangeRight);
            }
        } else {
            cityCodeArr[0] = formatCityCode(key / 1000000);
            cityCodeArr[1] = key;
        }
        if (cityCodeArr[1] > 0) {
            addr = StringUtils.replaceOnce(addr, getCityAddr(cityCodeArr[1]), "");
            Integer rangeLeft = formatCityCode(cityCodeArr[1] / 10000) + 1;
            Integer rangeRight = formatCityCode(cityCodeArr[1] / 10000) + 9999;
            cityCodeArr[2] = getTownCodeByAddr(addr, rangeLeft, rangeRight);
        }
        return cityCodeArr;
    }

    /**
     * 返回预定范围内匹配的镇区编码
     *
     * @param addr
     *            地址字符串
     * @param rangeLeft
     *            范围下边界
     * @param rangeRight
     *            范围上边界
     * @return
     */
    private static Integer getTownCodeByAddr(String addr, Integer rangeLeft, Integer rangeRight) {
        if (StringUtils.isNotBlank(addr)) {
            Map<Integer, Map<String, String>> townMap = OptionTownMap.OPT_MAP_TOWN;
            return code(addr, rangeLeft, rangeRight, townMap);
        }
        return 0;
    }

    /**
     * 返回预定范围内匹配的城市编码
     *
     * @param addr
     *            地址字符串
     * @param rangeLeft
     *            范围下边界
     * @param rangeRight
     *            范围上边界
     * @return
     */
    private static Integer getCityCodeByAddr(String addr, Integer rangeLeft, Integer rangeRight) {
        if (StringUtils.isNotBlank(addr)) {
            Map<Integer, Map<String, String>> cityMap = OptionCityMap.OPT_MAP_CITY;
            return code(addr, rangeLeft, rangeRight, cityMap);
        }
        return 0;
    }

    /**
     * 获取省/直辖市
     *
     * @param key
     *            地区编码
     * @param type
     *            显示类型：name-中文地址,en-英文地址
     * @return
     */
    private static String getProvStr(Integer key, String type) {
        Map<Integer, Map<String, String>> cityMap = OptionCityMap.OPT_MAP_CITY;
        if (key != null && ObjectUtils.toString(key, "").length() >= 4) {
            Integer cityCode = formatCityCode(getProvCodeBits(String.valueOf(key)));
            Map<String, String> cityDataMap = cityMap.get(cityCode);
            return cityDataMap != null ? cityDataMap.get(StringUtils.defaultIfBlank(type, "name")) + cityDataMap.get("s") : "";
        }
        return "";
    }

    /**
     * 获取市区
     *
     * @param key
     *            地区编码
     * @param type
     *            显示类型：name-中文地址,en-英文地址
     * @param ignoreFlag
     *            是否显示省/直辖市编码：true-显示,false-不显示
     * @return
     */
    private static String getCityStr(Integer key, String type, boolean ignoreFlag) {
        Map<Integer, Map<String, String>> cityMap = OptionCityMap.OPT_MAP_CITY;
        if (key != null && ObjectUtils.toString(key, "").length() >= 4) {
            Integer cityCode = formatCityCode(getCityCodeBits(String.valueOf(key)));
            if (ignoreFlag || (!CENTRAL_CITY_LIST.contains(cityCode) && (cityCode / 10000 % 100 > 0))) {
                Map<String, String> cityDataMap = cityMap.get(cityCode);
                return cityDataMap != null ? cityDataMap.get(StringUtils.defaultIfBlank(type, "name")) + cityDataMap.get("s") : "";
            }
        }
        return "";
    }

    /**
     * 获取镇区
     *
     * @param key
     *            地区编码
     * @param type
     *            显示类型：name-中文地址,en-英文地址
     * @return
     */
    public static String getTownStr(Integer key, String type) {
        Map<Integer, Map<String, String>> townMap = OptionTownMap.OPT_MAP_TOWN;
        if (key != null && ObjectUtils.toString(key, "").length() == 8) {
            if (key / 100 % 100 > 0) {
                Map<String, String> townDataMap = townMap.get(key);
                return townDataMap != null ? townDataMap.get(StringUtils.defaultIfBlank(type, "name")) : "";
            }
        }
        return "";
    }

    /**
     * townListByAddcode("10010","10020")
     * @param codeStr
     * @return
     */
    public static List<Map<String, String>> townListByAddcode(Integer[] codeStr) {

        List<Map<String, String>> townList = Lists.newArrayList();
        for (Integer s : codeStr) {
            Map<String, String> townMap = new HashMap<String, String>();
            String townName = null;
            if (OptionCityMap.isTown(s)) {
                townName = getTownStr(s, "name");
            } else {
                townName = getCityStr(s, "name", true);
            }
            if (StringUtils.isNotEmpty(townName)) {
                if (StringUtils.length(townName) > 2 && !townName.contains("市区") && !townName.contains("镇镇")) {
                    townName = StringUtils.replace(townName, "区", "");
                    townName = StringUtils.replace(townName, "县", "");
                    townName = StringUtils.replace(townName, "镇", "");
                    townName = StringUtils.replace(townName, "市", "");
                }
                townMap.put("id", String.valueOf(s));
                townMap.put("name", townName);
                townList.add(townMap);
            }
        }

        return townList;
    }



    public static void main(String[] args) {
        System.out.println(getTownCodeByCityCodeAndTownEn(1401, "humen"));
    }

    private static Integer code(String addr, Integer rangeLeft,Integer rangeRight, Map<Integer, Map<String, String>> paramMap) {
        for (Map.Entry<Integer, Map<String, String>> entry : paramMap.entrySet()) {
            Integer code = entry.getKey();
            Map<String, String> cityDataMap = entry.getValue();
            if (NumberUtils.toInt(String.valueOf(rangeLeft), 0) > 0 && code < rangeLeft) {
                continue;
            }
            if (NumberUtils.toInt(String.valueOf(rangeRight), 0) > 0 && code > rangeRight) {
                continue;
            }
            if (StringUtils.contains(addr, cityDataMap.get("name"))) {
                return code;
            }
        }
        return 0;
    }
}
